/* MainView.h - The Main UI for iSpotter.
 *
 * Copyright (C) 2008 Yuuichi Teranishi
 *
 * This file is derived from Stumbler, a program developed by iphone-wireless
 * project.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2
 * published by the Free Software Foundation.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; If not, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
#import <UIKit/UIKit.h>
#import <UIKit/UIPreferencesTableCell.h>
#import <UIKit/UIPreferencesTextTableCell.h>
#import <UIKit/UIAlertSheet.h>
#import <GraphicsServices/GraphicsServices.h>

#import "MainView.h"

#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dlfcn.h>
#include <math.h>

#define pi 3.14159265358979323846

#define MAX_LINES 29

#define UIMouseDown                     1
#define UIMouseDragged                1<<2  //within active area of control
#define UIMouseExitedDragged     1<<3  //move outside active area 
#define UIMouseEntered                  1<<4 //move crossed into active area
#define UIMouseExited                   1<<5 //move crossed out of active area
#define UIMouseUp                   1<<6 //up within the active area
#define UIMouseExitedUp              1<<7 //up outside active area

#define kAppNetwork     CFSTR("com.apple.preferences.network")
#define kKeyWifiNetwork CFSTR("wifi-network")

struct iSpotter_Preferences preferences;

static NSObject *str2double(char *str) {
  float ret;
  sscanf(str, "%f", &ret);
  return [NSNumber numberWithFloat: ret];
}

void setDefaultPreferences() {
  preferences.polling = 0;
  preferences.ignore = 1;
  preferences.zoom = 2;
  preferences.tracking = 1;
  preferences.movedist = 1; // * 10 meter
  preferences.caching = 1;
  preferences.direct = 0;
  preferences.pengine = 1;
  preferences.wifi = 0;
}

int loadPreferences() {
  FILE *f;
  int r;

  setDefaultPreferences();

  /* Load Preferences */
  f = fopen("/var/mobile/Library/Preferences/spotter.pref", "rb");
  if (!f) 
    return -1;
  r = fread(&preferences, sizeof(preferences), 1, f);
  fclose(f);
  if (!r) 
    setDefaultPreferences();
  return (r) ? 0 : -1;
}

int savePreferences() {
  FILE *f;
  int r;

  f = fopen("/var/mobile/Library/Preferences/spotter.pref", "wb");
  if (!f) return -1;
  r = fwrite(&preferences, sizeof(preferences), 1, f);
  fclose(f);
  return (r) ? 0 : -1;
}

int apLocSignalSort(APLocation *location1, APLocation *location2,
		    void *ignore)
{
  return [location1 signal] - [location2 signal];
}

Location *lineDivision (APLocation *location1,
			APLocation *location2)
{
  double x = [location1 longitude] +
    (([location2 longitude] - [location2 longitude]) *
     ([location2 signal] /
      (double) ([location1 signal] +
		[location2 signal])));
  double y = [location1 latitude] +
    (([location2 latitude] - [location2 latitude]) *
     ([location2 signal] /
      (double) ([location1 signal] +
		[location2 signal])));
  return [[Location alloc] initWithLat: y lng: x];
}

Location *triangulation (APLocation *location1,
			 APLocation *location2,
			 APLocation *location3)
{
  double x0 = [location1 longitude];
  double y0 = [location1 latitude];
  int sig0 = [location1 signal];
  double x1 = [location2 longitude];
  double y1 = [location2 latitude];
  int sig1 = [location2 signal];
  double x2 = [location3 longitude];
  double y2 = [location3 latitude];
  int sig2 = [location3 signal];

  // all the same
  if (((x0 == x1) && (y0 == y1)) &&
      ((x1 == x2) && (y1 == y2))) {
    return [[Location alloc] initWithLat: y0 lng: x0];
  }
  if ((x0 == x1) && (y0 == y1)) {
    if (sig0 >= sig1) {
      return lineDivision (location1, location3);
    }
    else {
      return lineDivision (location2, location3);
    }
  }
  if ((x0 == x2) && (y0 == y2)) {
    if (sig0 >= sig2) {
      return lineDivision (location1, location2);
    }
    else {
      return lineDivision (location3, location2);
    }
  }
  if ((x1 == x2) && (y1 == y2)) {
    if (sig1 >= sig2) {
      return lineDivision (location2, location1);
    }
    else {
      return lineDivision (location3, location1);
    }
  }
  double p = x0 + ((x1 - x0) * (sig1 / (double)(sig0 + sig1)));
  double q = y0 + ((y1 - y0) * (sig1 / (double)(sig0 + sig1)));
  double r = x0 + ((x2 - x0) * (sig2 / (double)(sig0 + sig2)));
  double s = y0 + ((y2 - y0) * (sig2 / (double)(sig0 + sig2)));

  double x = ((y2 - y0) * (y1 - y0) * (q - s) - (y2 - y0) * (x0 - x1) * p
	      + (y1 - y0) * (x0 - x2) * r) /
    (((x0 - x2) * (y1 - y0)) - ((x0 - x1) * (y2 - y0)));
  double y;
  if (y2 != y0) {
    y = (x0 - x2) * x / (y2 - y0) + (s - (x0 - x2) * r / (y2 - y0));
  }
  else {
    y = (x0 - x1) * x / (y1 - y0) + (s - (x0 - x1) * r / (y1 - y0));
  }
  return [[Location alloc] initWithLat: y lng: x];
}

// A = 100, B = 40
double dist (double sig) {
  return pow (10, (100 - sig) / 40);
}

/* 
 * Calculate distance from longitude and latitude
 * From sample code on http://www.zipcodeworld.com
 */

double deg2rad(double deg) {
  return (deg * pi / 180);
}

double rad2deg(double rad) {
  return (rad * 180 / pi);
}

// distance in m.
double distance(double lat1, double lon1, double lat2, double lon2) {
  //  NSLog(@"%f,%f,%f,%f", lat1, lon1, lat2, lon2);
  return rad2deg(acos(sin(deg2rad(lat1)) * sin(deg2rad(lat2)) +
		      cos(deg2rad(lat1)) * cos(deg2rad(lat2)) *
		      cos(deg2rad(lon1 - lon2)))) * 60 * 1.1515 * 1609.344;
}

@implementation MainView 

- (id)initWithFrame:(struct CGRect)windowRect delegate:(id) delegate logDB: (LogDB *)logdb {

  //  learnBrowserInitialize = NO;
  isPreference = NO;
  scanningLock = [[NSObject alloc] init];
  @synchronized(scanningLock){
    scanning = NO;
  }
  _logdb = logdb;
  _delegate = delegate;
  networksManager = [[MSNetworksManager alloc] init];
  loadPreferences();

  if ((self == [ super initWithFrame: windowRect ]) != nil) {
    frame = windowRect;

    navBar = [ self createNavBar ];

    transitionView = [ self createTransitionView ];
    detectionBrowser = [ self createDetectionBrowser ];
    //    learnBrowser = [ self createLearnBrowser ];
    historyBrowser = [ self createHistoryBrowser ];
    historyDate = [[NSCalendarDate alloc] init];
    tracking = YES;

    messageArea = [ self createMessageArea ];

    [ messageArea setAlpha: 0.65 ];
    [ historyBrowser addSubview: messageArea ];
    
    siteBrowser = [ self createSiteBrowser ];

    [ siteBrowser setOpenURLDelegate: delegate];
    [ siteBrowser reloadData ];


    logBrowser = [ self createLogBrowser ];
    prefsTable = [self createPrefPane ];

    [ self addSubview: transitionView ];

    //[ transitionView transition:1 toView: learnBrowser ];

    buttonBar = [ self createButtonBar: transitionView ];

    [ self addSubview: buttonBar ];
    [ self addSubview: navBar ];
    [ transitionView transition:2 toView: detectionBrowser ];
    currentView = CB_DETECT;
  }

  registerSheet = [[UIAlertSheet alloc] 
		    initWithTitle:@""
		    buttons: [NSArray arrayWithObjects:@"OK", @"Cancel", nil]
		    defaultButtonIndex:1 delegate:self context:self];

  alertSheet = [[UIAlertSheet alloc] initWithTitle:@"Alert"
				     buttons:[NSArray arrayWithObjects:@"OK", nil]
				     defaultButtonIndex:0 delegate:self context:self];
  db = [LockyDB alloc];
  //  [db open: @"/Applications/iLocky.app/locky"];
  if (![db open: @"/var/mobile/iSpotter/locky"] &&
      ![db open: @"/var/mobile/Library/Preferences/locky"]
      ) {
    db = nil;
    // Allert sheet displayed at centre of screen.
//     NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
//     UIAlertSheet *errSheet = [[UIAlertSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:1 delegate:self context:self];
//     [errSheet setBodyText:@"Cannot find LockyDB. You need to put locky.db at /var/mobile/iSpotter/locky.db"];
//     [errSheet popupAlertAnimated:YES];
  }
  wdb = [[WifiDB alloc] initWithFile: @"/var/mobile/iSpotter/spotter_wifi.db"];

  x = y = f = lastX = lastY = lastF = 0;

  return self;
}

- (void)messageAreaExecJavaScript:(NSString *)script {
  [ self performSelectorOnMainThread:
	   @selector(messageAreaExecJavaScriptOnMainThread:)
	 withObject: script waitUntilDone:YES ];
}

-(NSString *)messageAreaExecJavaScriptOnMainThread:(NSString *)script {
  return [[messageArea webView]
	   stringByEvaluatingJavaScriptFromString: script];
}

- (void)alertSheet:(UIAlertSheet*)sheet buttonClicked:(int)button
{
  if (sheet == registerSheet) {
    if ( button == 1 ) {
      Location *loc = regLocation;
      NSArray *aps = regAPs;
      int i;
      int news = 0, imps = 0;
      for (i = 0; i < [aps count]; i++) {
	NSDictionary *ap = [aps objectAtIndex: i];
	int ret = [wdb foundAPWithBSSID: [MSNetworksManager zeroPaddingBSSID:
							      [ap objectForKey:@"BSSID"]]
		       ssid: [ap objectForKey:@"SSID_STR"]
		       lng: [loc longitude]
		       lat: [loc latitude]
		       floor: regFloor
		       signal: [[ap objectForKey:@"RSSI"] intValue]
		   ];
	if (ret == 0) { // new
	  news++;
	}
	else { // improved
	  imps++;
	}
      }
      [ registerSheet dismissAnimated:YES ];
      NSString *body = @"";
      if (news != 0) {
	body = [body stringByAppendingFormat:@"Found %d new AP(s)\n", news];
      }
      if (imps != 0) {
	body = [body stringByAppendingFormat:@"%d AP(s) are improved\n", imps];
      }
      [alertSheet setTitle:@"Learned"];
      [alertSheet setBodyText:body];
      [alertSheet popupAlertAnimated:YES];
    }
    if ( button == 2 ) {
      [ registerSheet dismissAnimated:YES];
    }
  }
  else if (sheet == alertSheet) {
    // Do nothing.
    [alertSheet dismissAnimated:YES];
  }
  else {
    if ( button == 1 ) {
      exit(1);
    }
  }
}

- (void) start 
{
  scanTimer = nil;
  progressTimer = nil;
  [self restartScanPolling];
}

- (void)restartScanPolling {
  int interval = -1;

  switch (preferences.polling) {
  case 1:
    interval = 60;
    break;
  case 2:
    interval = 600;
    break;
  }
  if (scanTimer != nil) {
    [scanTimer invalidate];
    scanTimer = nil;
  }
  if (progressTimer != nil) {
    [progressTimer invalidate];
    progressTimer = nil;
  }
  if (interval > 0) {
    scanTimer = [NSTimer scheduledTimerWithTimeInterval:interval
			 target:self
			 selector:@selector(scan)
			 userInfo:nil
			 repeats:YES];
    // redisplay
    [self startDisplayNextAutoScan];
    progressTimer = [NSTimer scheduledTimerWithTimeInterval:1
			     target:self
			     selector:@selector(startDisplayNextAutoScan)
			     userInfo:nil
			     repeats:NO];
  }
  //  [self displayNextAutoScan];
}

- (void)startDisplayNextAutoScan {
  progressTimer = [NSTimer scheduledTimerWithTimeInterval:10
			   target:self
			   selector:@selector(displayNextAutoScan)
			   userInfo:nil
			   repeats:YES];
}

- (void)displayNextAutoScan {
  if (scanTimer != nil) {
    NSString *next =
      [NSString
	stringWithFormat:
	  @"nextDetection('Next detection within %d seconds')",
	((((int) [[scanTimer fireDate] timeIntervalSince1970]
	   - (int) [[[NSDate alloc] init] timeIntervalSince1970]) / 10) * 10) + 10];
    [detectionBrowser execJavaScript: next];
    [self messageAreaExecJavaScript: next];
  }
  else {
    [detectionBrowser execJavaScript: @"nextDetection('Autoscan disabled')"];
  }
}

- (void)detect {
  [detectionBrowser execJavaScript: @"detect()"];
}

- (void)displayDetectHTML {
  [detectionBrowser
    loadURL:
      [NSURL URLWithString: 
	       @"file:///Applications/iSpotter.app/detect.html"]];
}

- (void)dealloc {
  [ detectionBrowser dealloc ];
  //  [ learnBrowser dealloc ];
  [ prefsTable release ];
  [ transitionView release ];
  [ navBar release ];
  [ super dealloc ];
}

- (void) setMapLauncher: (id) launcher { 
  mapLauncher = launcher;
}

- (SimpleWebView *) createDetectionBrowser {
  // create text view
  CGSize navBarSize = [UINavigationBar defaultSize];
  SimpleWebView* view = [[SimpleWebView alloc] initWithFrame: CGRectMake(0, 0, frame.size.width * 2, (frame.size.height - navBarSize.height) * 2) webDelegate: self withSmoothScroll: NO];

  [NSTimer scheduledTimerWithTimeInterval:0.4
	   target:self
	   selector:@selector(displayDetectHTML)
	   userInfo:nil
	   repeats:NO];

  return view;
}

- (SiteBrowser *) createSiteBrowser {
  CGSize navBarSize = [UINavigationBar defaultSize];
  SiteBrowser *browser = [[SiteBrowser alloc] 
			   initWithFrame:
			     CGRectMake(0, 0, frame.size.width,
					frame.size.height - navBarSize.height)];
  return browser;  
}

- (LogBrowser *) createLogBrowser {
  CGSize navBarSize = [UINavigationBar defaultSize];
  LogBrowser *browser = [[LogBrowser alloc] 
			  initWithFrame:
			    CGRectMake(0, 0, frame.size.width,
				       frame.size.height - navBarSize.height)];
  [browser setLogDB: _logdb];
  [browser setDelegate: self];
  return browser;
}

- (SimpleWebView *) createHistoryBrowser {
  CGSize navBarSize = [UINavigationBar defaultSize];
  SimpleWebView* webView = [[SimpleWebView alloc]
			     initWithFrame: CGRectMake(0, 0, 
						       frame.size.width,
						       frame.size.height - navBarSize.height)
			     webDelegate: self withSmoothScroll: YES];
  [webView setTapDelegate: self];
  return webView;
}

- (UIWebView *) createMessageArea {
  CGSize navBarSize = [UINavigationBar defaultSize];
  UIWebView* webView = [[UIWebView alloc]
			 initWithFrame: CGRectMake(0, 
						   0,
						   //frame.size.height - navBarSize.height - 25 - 48,
						   frame.size.width,
						   25)];
  [webView setTapDelegate: self];
  WebView* mCoreWebView = [webView webView];
  [mCoreWebView setFrameLoadDelegate: self];
  [mCoreWebView setPolicyDelegate: self];
  [mCoreWebView setUIDelegate: self];
  [mCoreWebView setResourceLoadDelegate: self];

  return webView;
}

- (void)doubleTapped {
  [ logBrowser reloadData ];
  [ transitionView transition:3 toView:logBrowser ];
  [ navItem setTitle: @"Dates" ];
  [ navBar showButtonsWithLeftTitle:@"Back"
	   rightTitle:nil leftBack: NO ];
  [buttonBar removeFromSuperview];
  currentView = CB_DATES;
}

- (void)webDoubleTapped {
  [historyBrowser execJavaScript:@"map.zoomIn();"];
}

- (void)webTwoTapped {
  [historyBrowser execJavaScript:@"map.zoomOut();"];
}

- (void)webThreeTapped {
  [historyBrowser execJavaScript:@"moveTo(lastX, lastY, lastF);"];
}

- (void) scrollThread: (NSString *) script {
  NSAutoreleasePool* pool;
  pool = [[NSAutoreleasePool alloc]init];
  [historyBrowser execJavaScript: script];
  [pool release];
  [NSThread exit];  
}


// Difficult...
- (void)draggedWithX: (int) x withY: (int) y {
  //NSLog(@"x=%f, y=%f", x, y);
  //  CGSize navBarSize = [UINavigationBar defaultSize];
  
//   NSLog([NSString stringWithFormat:
// 		    @"setCenterPixel(%d,%d)", 
// 		  (((int)frame.size.width) / 2) - x,
// 		  (((int)(frame.size.height - navBarSize.height)) / 2) - y]);
//   [NSThread detachNewThreadSelector:@selector(scrollThread:)
// 	    toTarget:self withObject:
// 	      [NSString stringWithFormat:
// 			  @"setCenterPixel(%d,%d)",
// 			(((int)frame.size.width) / 2) - x,
// 			(((int)(frame.size.height - navBarSize.height)) / 2) - y]];
}

- (void)dateSelected:(NSCalendarDate *)date {
  historyDate = [date copy];
  NSCalendarDate *now;
  now = [[NSCalendarDate alloc] init];
  if (([historyDate yearOfCommonEra] == [now yearOfCommonEra]) &&
      ([historyDate monthOfYear] == [now monthOfYear]) &&
      ([historyDate dayOfMonth] == [now dayOfMonth])) {
    tracking = YES;
  }
  else {
    tracking = NO;
  }
  [ transitionView transition:7 toView:historyBrowser ];
  [self redrawTrails: [date
			descriptionWithCalendarFormat:@"%Y/%m/%d"
			timeZone:nil
			locale:nil
		       ]];
  currentView = CB_HISTORY;
  [ self setNavBar];
  [ self addSubview: buttonBar ];  
}

- (void) historyBrowserExecJavaScriptOnThread:(NSString *)script {
  NSAutoreleasePool* pool;
  pool = [[NSAutoreleasePool alloc]init];
  [ historyBrowser execJavaScriptOnCurrentThread: script ];
  [ historyBrowser gotoCenter];
  [pool release];
  [NSThread exit];
}

- (void) simpleWebView:(SimpleWebView *)view movedToX: (int) x toY: (int) y {
  NSString *script = [NSString stringWithFormat: @"setCenterPixel(%d,%d)", (int)x, (int)y];

  // XX This causes bus error.
  //  [NSThread detachNewThreadSelector:@selector(historyBrowserExecJavaScriptOnThread:)
  //	    toTarget:self withObject:script];  

  [ historyBrowser execJavaScriptOnCurrentThread: script ];
  [ historyBrowser gotoCenter];
}


- (void) simpleWebView:(SimpleWebView *)view loadFinished:(NSURL *)url {
  if (view == detectionBrowser) {
    [self scan];
  }
  if (view == historyBrowser) {
    [ historyBrowser execJavaScript:
		       [NSString stringWithFormat:
				   @"enableTracking(%d)",
				 preferences.tracking ]];
    // start thread
    [ self startWithTrailsForDate:
	     [[[NSDate alloc] init]
	       descriptionWithCalendarFormat:@"%Y/%m/%d"
	       timeZone:nil
	       locale:nil
	      ]];
    
  }
}

// - (SimpleWebView *) createLearnBrowser {
//   // create text view
//   CGSize navBarSize = [UINavigationBar defaultSize];
//   SimpleWebView* webView = [[SimpleWebView alloc] initWithFrame: CGRectMake(0, 0, frame.size.width, frame.size.height - navBarSize.height) progressDelegate: self];
//   return webView;
// }

- (UIButtonBar *)createButtonBar:(UIView *)view {
    UIButtonBar *button;
    button = [ [ UIButtonBar alloc ] 
       initInView: view
       withFrame: CGRectMake(0.0f, 411.0f, 320.0f, 49.0f)
       withItemList: [ self buttonBarItems ] ];
    [button setDelegate:self];
    [button setBarStyle:1];
    [button setButtonBarTrackingMode: 2];

    int buttons[3] = { 1, 2, 3 };
    [button registerButtonGroup:0 withButtons:buttons withCount: 3];
    [button showButtonGroup: 0 withDuration: 0.0f];
    int tag;

    //    for(tag = 1; tag < 5; tag++) {
    //        [ [ button viewWithTag:tag ] 
    //            setFrame:CGRectMake(((tag - 1) * 80), 1.0, 80.0, 48.0)
    //        ];
    //    }
    [ button showSelectionForButton: 1];

    return button;
}

// As a WebView delegate.
+ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector { return NO; }

- (void)webView:(WebView *)sender windowScriptObjectAvailable:(id)windowScriptObject
{
  // add the key-value notification object to the script environment
  [windowScriptObject setValue:self forKey:@"mainview"];
}

- (void)gotoFloor:(int)floor {
  [self messageAreaExecJavaScript:
	  [NSString stringWithFormat:
		      @"gotoFloor(%d)", floor]];
}

- (void)setFloor:(int)floor {
  [historyBrowser execJavaScript:
		    [NSString stringWithFormat:
				@"setFloor(%d)", floor]];
}

- (void)setNavBar {
  NSString *label;
  switch (currentView) {
  case CB_DETECT:
    [ navItem setTitle: @"iSpotter" ];
    [ navBar showButtonsWithLeftTitle:nil
	     rightTitle:@"Settings" leftBack: NO
      ];
    break;
  case CB_HISTORY:
    if (tracking) {
      label = @"Map";
    }
    else {
      label = [historyDate
		descriptionWithCalendarFormat:@"%Y-%m-%d"
		timeZone:nil
		locale:nil
	       ];
    }
    [ navItem setTitle: label];
    
    [ navBar showButtonsWithLeftTitle:@"Learn"
	     rightTitle:@"Settings" leftBack: NO];

    break;
  case CB_LEARN:
    [ navItem setTitle: @"Learn" ];
    [ navBar showButtonsWithLeftTitle:@"Reload"
	     rightTitle:@"Settings" leftBack: NO];
    break;
  case CB_WEB:
    [ navItem setTitle: @"Web Sites" ];
    [ navBar showButtonsWithLeftTitle:nil
	     rightTitle:@"Settings" leftBack: NO];
    break;
  }

}

- (void)navigationBar:(UINavigationBar*)navbar buttonClicked:(int)button 
{
  if (button == 1) {
    if (isPreference) {
      isPreference = NO;
      preferences.polling = [ pollingControl selectedSegment ];
      preferences.zoom = [ zoomControl selectedSegment ];
      preferences.ignore = [ ignoreControl value ];
      preferences.tracking = [ trackingControl value ];
      preferences.caching = [ cachingControl value ];
      preferences.movedist = [ distanceControl selectedSegment ];
      preferences.direct = [ directControl value ];
      preferences.pengine = [ pengineControl value ];
      preferences.wifi = [ wifiControl value ];
      savePreferences();
      [self restartScanPolling];
      [self displayNextAutoScan];
      [ historyBrowser execJavaScript:
			 [NSString stringWithFormat:
				     @"enableTracking(%d)",
				   preferences.tracking ]];
      // Its annoying..
      //      [ historyBrowser execJavaScript:
      //			 [NSString stringWithFormat:
      //				     @"setZoom(%d)",
      //				   preferences.zoom ]];
      switch (currentView) {
      case CB_DETECT:
	[ transitionView transition:2 toView:detectionBrowser ];
	break;
      case CB_HISTORY:
	[ transitionView transition:2 toView:historyBrowser ];
	break;
      case CB_LEARN:
	//	[ transitionView transition:2 toView:learnBrowser ];
	break;
      case CB_WEB:
	[ transitionView transition:2 toView:siteBrowser ];
	break;
      }
      [self setNavBar];
      [self addSubview: buttonBar];
    }
    else if (currentView == CB_HISTORY) {
      double clng = [[historyBrowser execJavaScriptOnCurrentThread:@"currentLng();"] doubleValue];
      double clat = [[historyBrowser execJavaScriptOnCurrentThread:@"currentLat();"] doubleValue];
      int floor = [[historyBrowser execJavaScriptOnCurrentThread:@"currentFloor();"] intValue];
      NSMutableArray *aps = [[NSMutableArray alloc] init];
      NSArray *aps_orig = [self scanWiFi];
      int i;

      for (i = 0; i < [aps_orig count]; i++) {
	// 	    NSLog([MSNetworksManager zeroPaddingBSSID:
	// 				       [[aps_orig objectAtIndex: i] objectForKey:@"BSSID"]]);
	[aps addObject: [NSDictionary dictionaryWithObjectsAndKeys:
					[[[aps_orig objectAtIndex: i] objectForKey:@"BSSID"] copy], @"BSSID",
				      [[[aps_orig objectAtIndex: i] objectForKey:@"RSSI"] copy], @"RSSI",
				      [[[aps_orig objectAtIndex: i] objectForKey:@"SSID_STR"] copy], @"SSID_STR", 
				      nil]];
      }
      NSString *fstr;
      if (floor == 0)
	fstr = @"OutDoor";
      else if (floor < 0)
	fstr = [NSString stringWithFormat: @"B%dF", abs(floor)];
      else {
	fstr = [NSString stringWithFormat: @"%dF", floor];
      }
      if ([aps count] == 0) {
	[alertSheet setTitle:nil];
	[alertSheet setBodyText:@"No AP found"];
	[alertSheet popupAlertAnimated:YES];
      }
      else {
	regAPs = aps;
	regLocation = [[Location alloc] initWithLat: clat lng: clng];
	regFloor = floor;
	
	[registerSheet setTitle: [NSString stringWithFormat: @"Learn %d AP(s)?", [aps count]]];
	[registerSheet setBodyText: [NSString stringWithFormat:  @"%f\n%f\n%@", clng, clat, fstr]];
	[registerSheet popupAlertAnimated:YES];
      }
    }
    else if (currentView == CB_LEARN) {
      //[_delegate showProgress: @"Loading..."];
      //      [learnBrowser loadURL:
      //		      [NSURL URLWithString: 
      //			       @"file:///Applications/iSpotter.app/learn.html"]];
    }
    else if (currentView == CB_DATES) {
      [ transitionView transition:7 toView:historyBrowser ];
      currentView = CB_HISTORY;
      NSDate *now = [[NSCalendarDate alloc] init];
      if (([historyDate yearOfCommonEra] == [now yearOfCommonEra]) &&
	  ([historyDate monthOfYear] == [now monthOfYear]) &&
	  ([historyDate dayOfMonth] == [now dayOfMonth])) {
	tracking = YES;
      }
      else {
	tracking = NO;
      }
      [ self setNavBar];
      [ self addSubview: buttonBar ];  
    }
  }
  else {
    isPreference = YES;
    [ transitionView transition:1 toView: prefsTable ];
    //    [self hideButtons];
    [ navItem setTitle: @"Settings" ];
    [ navBar showButtonsWithLeftTitle:@"Back"
	     rightTitle:nil leftBack: YES ];
    [buttonBar removeFromSuperview];
  }

}

- (int)permissionForSite:(NSString *)site {
  return [siteBrowser permissionForSite:site];
}

- (void)addSite:(NSString *)site permission:(BOOL)permission {
  [siteBrowser addSite: site permission: permission ];
  if (currentView == CB_WEB) {
    [ siteBrowser reloadData ];
  }
}

- (void) buttonEvent:(UIPushButton *)button {
  if (button == detectButton) {
    if ([self scan] == NO) {
    }      
  }
  else {
    [historyBrowser execJavaScript:
		      [@"moveTo"
			stringByAppendingFormat: @"(%f, %f)", x, y]];
    [mapLauncher showMapLat: y lng: x];
  }

}

- (UINavigationBar *)createNavBar {
  CGSize navBarSize = [UINavigationBar defaultSize];

  UINavigationBar *nav = [[ UINavigationBar alloc ] initWithFrame:
						      CGRectMake(frame.origin.x, frame.origin.y, frame.size.width, navBarSize.height)];

  [nav setDelegate: self];
  [nav setTapDelegate: self];

  [nav setBarStyle: 1];
  //  [ self setNavBar ];
  [nav showButtonsWithLeftTitle:nil rightTitle:@"Settings" leftBack: NO]; 

  [nav pushNavigationItem: navItem = [[UINavigationItem alloc]
				       initWithTitle: @"iSpotter"]];
  return nav;
}

- (void)view:(UIView *)view handleTapWithCount:(int)count event:(GSEvent *)event{ 
  if (count == 2) {
    [self doubleTapped];
  }
}

- (UITransitionView *)createTransitionView {
  CGSize navBarSize = [UINavigationBar defaultSize];

  UITransitionView *transition =
    [[ UITransitionView alloc ] 
      initWithFrame:
	CGRectMake(frame.origin.x, navBarSize.height, frame.size.width,
		   frame.size.height - navBarSize.height)
     ];

  [transition setDelegate:self];

  return transition;
}

- (void)didDismissAlertSheet:(id)aSheet {
}

- (void)didPresentAlertSheet:(id)aSheet {
}

- (void)transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
}

- (int)getCurrentView {
  return currentView;
}

- (BOOL)scanWithReplyDelegate:(id) replyDelegate {
  [NSThread detachNewThreadSelector:@selector(scanThread:)
	    toTarget:self withObject:replyDelegate];
}

- (BOOL)scan {
  [NSThread detachNewThreadSelector:@selector(scanThread:)
	    toTarget:self withObject:nil];
}

- (void)scanThread:(id) replyDelegate {
  NSAutoreleasePool* pool;
  pool = [[NSAutoreleasePool alloc]init];
  
  [_delegate setStatusBarShowsProgress:YES];
  [detectionBrowser execJavaScript: @"showProgress('Detecting...');"];
  [self messageAreaExecJavaScript: @"message('Detecting...');"];  

  [ self scanInternal: replyDelegate];

  [_delegate setStatusBarShowsProgress:NO];
  [pool release];
  [NSThread exit];  
}


// From APLogger;
- (void)setWiFiOn:(BOOL)on
{
    void *libHandle =
      dlopen("/System/Library/Frameworks/Preferences.framework/Preferences",
	     RTLD_LAZY);
    int (*enable)(BOOL mode) = dlsym(libHandle, "_SetWiFiEnabled");

    if(on) {
      enable(YES);
      CFPreferencesSetAppValue(kKeyWifiNetwork, kCFBooleanTrue, kAppNetwork);
    }
    else {
      enable(NO);
      CFPreferencesSetAppValue(kKeyWifiNetwork, kCFBooleanFalse, kAppNetwork);
    }
    // MAKE PREFERENCES TAKE EFFECT, e.g. WRITE TO DISK
    CFPreferencesAppSynchronize(kAppNetwork);
}

- (BOOL)isWiFiON
{
  CFPreferencesAppSynchronize(kAppNetwork);
  return (CFPreferencesCopyAppValue(kKeyWifiNetwork, kAppNetwork) ==
	  kCFBooleanTrue);
}

- (void) displayMessage: (NSString *) message {
  [detectionBrowser execJavaScript: [@"showProgress"
				      stringByAppendingFormat: @"('%@')", message]];
  [self messageAreaExecJavaScript: [@"message"
				     stringByAppendingFormat: @"('%@')", message]];
}

#define MAX_TRIAL 10

- (NSArray *)scanWiFi {

  @synchronized(scanningLock){
    if (scanning) {
      return nil;
    }
    scanning = YES;
  }
  BOOL success = NO;
  BOOL autoOn = NO;
  BOOL beforeWifi = [self isWiFiON];
  int count = 0;
  if (preferences.wifi != 0 && !beforeWifi) {
    [self setWiFiOn: YES];
    [self displayMessage: @"Force WiFi ON"];
  }
  else {
    [self displayMessage: @"Detecting..."];
  }
  @synchronized(networksManager) {
    [networksManager removeAllNetworks];
    while (((success = [networksManager scan]) == NO) &&
	   (count < MAX_TRIAL)) {
      count++;
      [self displayMessage: [NSString stringWithFormat:
					@"Detecting...(%dth retry);",
				      count]];
      sleep(1);
    }
  }
  if ((preferences.wifi != 0) && ([self isWiFiON] != beforeWifi)) {
    [self setWiFiOn: beforeWifi];
  }
  @synchronized(scanningLock){
    scanning = NO;
  }
  if (success) {
    return [[networksManager networks] allValues];
  }
  else {
    return nil;
  }
}

- (void)scanInternal: (id) replyDelegate {
  //  if (db == nil) {
    // Nothing to do when LockyDB does not exist.
  //    return;
  //  }
  NSString *ret = @"recvLoc(0, 0, -100, {});";
  NSArray *networks = [self scanWiFi];

  if (networks == nil) {
    [self displayMessage: @"Wifi is busy or not working"];
    if (replyDelegate != nil) {
      // Wifi not working.
      [replyDelegate replyString: @"recvLoc(0,0,-1,{})"];
    }
    return;
  }

  int i;
  NSString *log = [[NSString alloc] init];

  if ([networks count] == 0) {
    if (replyDelegate != nil) {
      // No AP found
      [replyDelegate replyString: @"recvLoc(0,0,-2,{})"];
    }
  }

  // Locky Stumbler Log:
  log = [log stringByAppendingString:
	       @"#LockyStumbler Log Version 2\n#\n#UPLOAD=F|ACCESSPOINT=FFFFF\n"];

  u_int64_t now_ms;
  char now_ms_str[256];
  struct timeval now;
  gettimeofday(&now, NULL);
  now_ms = (u_int64_t)now.tv_sec * 1000 + now.tv_usec / 1000;
  sprintf(now_ms_str, "%llu", now_ms);
  NSString *line = [[NSString alloc] init];

  int reg_count = 0;
  int unreg_count = 0;
  NSMutableArray *apls = [[NSMutableArray alloc] init];
  NSString *locStr = @"";
 
  NSDate *date = [[NSDate alloc] init];

  for (i = 0; i < [networks count]; i++) {
    NSDictionary *network = [networks objectAtIndex: i];
    [self displayMessage: [NSString stringWithFormat: @"Detecting...%d", i + 1]];

    // It may require some tuning for iPhone/iPod Wifi
    //int signal = ([[network objectForKey:@"RSSI"] intValue] * 75.0 /99.0) - 85.0;
    int signal = [[network objectForKey:@"RSSI"] intValue];

    // Locky Stumbler Log:
    log = [log stringByAppendingFormat:
		 @"TYPE=WIFI|TIME=%@|ID=%@|NAME=%@|RSSI=%d|WEP=%@|INFR=%@\n", 
	       [NSString stringWithCString: now_ms_str encoding: NSUTF8StringEncoding],
	       [MSNetworksManager zeroPaddingBSSID:
				    [network objectForKey:@"BSSID"]],
	       [network objectForKey:@"SSID_STR"],
	       (int)((signal * 75.0 /99.0) - 85.0),
	       [[network objectForKey:@"WEP"] boolValue] ? @"true":@"false",
	       [[network objectForKey:@"AP_MODE"] intValue] == 2 ? @"true" : @"false"];

    if ([[network objectForKey:@"AGE"] intValue] != 0) {
      //
    }
    APLocation *loc = nil;
    int type = 0;
    loc = [wdb getAPLocation:[MSNetworksManager zeroPaddingBSSID:
						  [network objectForKey:@"BSSID"]]
	       ssid: [network objectForKey:@"SSID_STR"]
	       signal: signal];
    if (loc == nil) {
      if (db != nil) {
	loc = [db getAPLocation:
		    [MSNetworksManager zeroPaddingBSSID:
					 [network objectForKey:@"BSSID"]]
		  ssid: [network objectForKey:@"SSID_STR"]
		  signal: signal];
	if (loc != nil) {
	  type = 1;
	}
      }
    }
    else {
      type = 2;
    }
    if (i < MAX_LINES) {
      line = [line stringByAppendingString: [self lineForRSSI: signal type: type]
	      ];
    }    
    if (loc != nil && ![apls containsObject:loc]) {
      [apls addObject: loc];
    }
  }
  //fprintf (stderr, "%s\n", [log cStringUsingEncoding: NSUTF8StringEncoding]);
  locStr = [locStr stringByAppendingFormat:
		     @"Registered APs: %d<BR>Unregistered APs: %d<BR>Total APs: %d<BR>",
		   [apls count], [networks count] - [apls count],
		   [networks count]];
  if ([apls count] > 0) {
    int i;
    double w = 0;
    double xp = 0;
    double yp = 0;
    double fp = 0;
    double d = 0;
    double ds = 0;
    [apls sortUsingFunction:&apLocSignalSort context:nil];

    for (i = 0; i < [apls count]; i++) {
      d = (double) dist([[apls objectAtIndex: i] signal]);
      ds += d;
      w += 1 / d;
      xp += (1 / d) * [[apls objectAtIndex: i] longitude];
      yp += (1 / d) * [[apls objectAtIndex: i] latitude];
      fp += (1 / d) * [[apls objectAtIndex: i] floor];
    }
    x = xp / w;
    y = yp / w;
    f = round(fp / w);
    ds = ds / [apls count];

    // record distance.
    ///    if ((lastX != x) && (lastY != y)) {
    double di = distance(lastY, lastX, y, x);
    if (lastF != f || di > preferences.movedist * 10) {
      // NSLog(@"Moved: %fm", di);
      [_logdb storeWithLng: y lat: x date: 
		[date timeIntervalSince1970]
	      precision:[apls count]];
    }
    else {
      //      NSLog(@"Not Moved: %fm", di);
    }

    ret = [NSString stringWithFormat:@"recvLoc(%f, %f, %.1f, {'addr':''});", x, y, ds];
    locStr = [locStr stringByAppendingFormat: @"<BR><font size=\"4\"color=\"cyan\">E: %f</font><BR><font size=\"4\" color=\"pink\">N: %f</font><BR>", x, y];
    if ([historyBrowser loaded]) {
      if (lastF != f) {
	[historyBrowser execJavaScript: 
			  [NSString stringWithFormat:@"gotoFloor(%d)", f]];
      }
      if (tracking)
	{
	  // XXX something wrong
	  if (!([historyDate timeIntervalSince1970] < [date timeIntervalSince1970] &&
		[date timeIntervalSince1970] < [historyDate timeIntervalSince1970] + 60 * 60 * 24)) {
	    [historyBrowser execJavaScript: @"clearTrails()"];
	    // update currently displaying date.
	    historyDate = date;
	  }
	  if (di > preferences.movedist * 10) { // check distance again
	    //NSLog([@"dropTrail"
	    //stringByAppendingFormat: @"(%f, %f, %d, %d)", x, y, f, [apls count]]);
	    [historyBrowser execJavaScript:
			      [@"dropTrail"
				stringByAppendingFormat: @"(%f, %f, %d, %d)", x, y, f, [apls count]]];
	  }
	  else {
	    [historyBrowser execJavaScript: @"staying()"];
	  }
	}
    }

    lastX = x;
    lastY = y;
    lastF = f;


    //    if ([learnBrowser loaded]) {
    //      [learnBrowser execJavaScript:
    //		      [@"setLatest"
    //			stringByAppendingFormat: @"(%f, %f)", x, y]];
    //    }
  }
  else {
    locStr = [locStr stringByAppendingString: @"<BR><font size=\"4\"color=\"gray\">Location detection failed</font><BR>"];
  }

  [detectionBrowser execJavaScript: [@"displayAPs"
				      stringByAppendingFormat: @"('%@')",
				      line]];
  [detectionBrowser execJavaScript: [@"displayInfo"
				      stringByAppendingFormat: @"('%@')",
				      locStr]];

  [self displayMessage: [NSString stringWithFormat:
				    @"Last Detection: %@", date ]];
  [self displayNextAutoScan];
  if (replyDelegate != nil) {
    [replyDelegate replyString: ret];
  }

  return;
}

- (void) startWithTrailsForDate: (NSString *) dateStr{
  [historyBrowser execJavaScript: @"$('map').innerHTML = ''"];
  [self messageAreaExecJavaScript: @"message('Retrieving...');"];
  [NSThread detachNewThreadSelector:@selector(startWithTrailsThread:)
	    toTarget:self withObject:dateStr];
}

- (void) startWithTrailsThread: (NSString *) dateStr {
  NSAutoreleasePool* pool;
  pool = [[NSAutoreleasePool alloc]init];
  NSString *result = [_logdb searchByDateForJSON:
			       [NSDate
				 dateWithNaturalLanguageString:
				   [dateStr
				     stringByAppendingString:
				       @" 0:00"]]];
  if (lastF != 0) {
    [historyBrowser execJavaScript: 
		      [NSString stringWithFormat:@"gotoFloor(%d)", lastF]];
  }
  [historyBrowser execJavaScript:
		    [@"startWithTrails" stringByAppendingFormat:
			@"(%@, %d)", result, preferences.zoom]];
  [pool release];
  [NSThread exit];  
}

- (void) redrawTrails: (NSString *) dateStr{
  [self messageAreaExecJavaScript: @"message('Retrieving...');"];
  [NSThread detachNewThreadSelector:@selector(redrawTrailsThread:)
	    toTarget:self withObject:dateStr];
}

- (void) redrawTrailsThread: (NSString *) dateStr {
  NSAutoreleasePool* pool;
  pool = [[NSAutoreleasePool alloc]init];
  NSString *result = [_logdb searchByDateForJSON:
			       [NSDate
				 dateWithNaturalLanguageString:
				   [dateStr
				     stringByAppendingString:
				       @" 0:00"]]];
  [self messageAreaExecJavaScript: @"message('Drawing...');"];
  [historyBrowser execJavaScript:
		    [@"redrawTrails" stringByAppendingFormat:
			@"(%@)", result]];
  [pool release];
  [NSThread exit];  
}

- (NSString *)lineForRSSI: (int) rssi type: (int) type
{
  NSString* t;
  switch (type) {
  case (0):
    t = @"-no";
    break;
  case (1):
    t = @"";
    break;
  case (2):
    t = @"1";
    break;
  }
  return [NSString stringWithFormat: @"<img style=\"vertical-align:bottom\" width=10 height=%d src=\"file:///Applications/iSpotter.app/line%@.png\">", rssi, t];
}

- (void)buttonBarItemTapped:(id) sender {
  int button = [ sender tag ];
  switch (button) {
  case 1:
    [ transitionView transition:6 toView:detectionBrowser ];
    currentView = CB_DETECT;
    break;
  case 2:
    if (historyBrowserInitialize == NO) {
      //[_delegate showProgress: @"Loading..."];
      [historyBrowser loadURL:
			[NSURL URLWithString: 
				 @"file:///Applications/iSpotter.app/maps.html"]];
      
      NSURLRequest* urlRequest = [NSURLRequest
				   requestWithURL:
				     [NSURL URLWithString: 
					      @"file:///Applications/iSpotter.app/message.html"]];
      [messageArea loadRequest: urlRequest];
      historyBrowserInitialize = YES;
    }
    [ transitionView transition:6 toView:historyBrowser ];
    //    if (currentView == CB_DETECT) {
    //      [self hideButtons];
    //    }
    currentView = CB_HISTORY;
    break;
    //  case 3:
    //    if (learnBrowserInitialize == NO) {
      //[_delegate showProgress: @"Loading..."];
    //      [learnBrowser loadURL:
    //		      [NSURL URLWithString: 
    //			       @"file:///Applications/iSpotter.app/learn.html"]];
    //      learnBrowserInitialize = YES;
    //    }
    //    [ transitionView transition:6 toView:learnBrowser ];
    //    if (currentView == CB_DETECT) {
    //      [self hideButtons];
    //    }
    //    currentView = CB_LEARN;
    //    break;
  case 3:
    [ siteBrowser reloadData ];
    [ transitionView transition:6 toView:siteBrowser ];
    //if (currentView == CB_DETECT) {
    //      [self hideButtons];
    //    }
    currentView = CB_WEB;
    break;
  }
  [self setNavBar];
}

- (NSArray *)buttonBarItems {
  return [ NSArray arrayWithObjects:
        [ NSDictionary dictionaryWithObjectsAndKeys:
          @"buttonBarItemTapped:", kUIButtonBarButtonAction,
          @"Search.png", kUIButtonBarButtonInfo,
          @"SearchSelected.png", kUIButtonBarButtonSelectedInfo,
          [ NSNumber numberWithInt: 1], kUIButtonBarButtonTag,
            self, kUIButtonBarButtonTarget,
          @"Detect", kUIButtonBarButtonTitle,
          @"0", kUIButtonBarButtonType,
          nil
        ],

        [ NSDictionary dictionaryWithObjectsAndKeys:
          @"buttonBarItemTapped:", kUIButtonBarButtonAction,
          @"History.png", kUIButtonBarButtonInfo,
          @"HistorySelected.png", kUIButtonBarButtonSelectedInfo,
          [ NSNumber numberWithInt: 2], kUIButtonBarButtonTag,
            self, kUIButtonBarButtonTarget,
          @"Map", kUIButtonBarButtonTitle,
          @"0", kUIButtonBarButtonType,
          nil 
        ], 

		   //        [ NSDictionary dictionaryWithObjectsAndKeys:
		   //          @"buttonBarItemTapped:", kUIButtonBarButtonAction,
		   //          @"MostRecent.png", kUIButtonBarButtonInfo,
		   //          @"MostRecentSelected.png", kUIButtonBarButtonSelectedInfo,
		   //          [ NSNumber numberWithInt: 3], kUIButtonBarButtonTag,
		   //            self, kUIButtonBarButtonTarget,
		   //          @"Learn", kUIButtonBarButtonTitle,
		   //          @"0", kUIButtonBarButtonType,
		   //          nil
		   //        ], 

        [ NSDictionary dictionaryWithObjectsAndKeys:
          @"buttonBarItemTapped:", kUIButtonBarButtonAction,
          @"TopRated.png", kUIButtonBarButtonInfo,
          @"TopRatedSelected.png", kUIButtonBarButtonSelectedInfo,
          [ NSNumber numberWithInt: 3], kUIButtonBarButtonTag,
            self, kUIButtonBarButtonTarget,
          @"Web Sites", kUIButtonBarButtonTitle,
          @"0", kUIButtonBarButtonType,
          nil 
        ],

        nil
    ];
}

- (UIPreferencesTable *)createPrefPane {
  CGSize navBarSize = [UINavigationBar defaultSize];
  UIPreferencesTable *pref = [[UIPreferencesTable alloc] initWithFrame:
							   CGRectMake(0, navBarSize.height, frame.size.width, frame.size.height - navBarSize.height)];
  
  [ pref setDataSource: self ];
  [ pref setDelegate: self ];

  int i, j;
  for(i=0;i<10;i++)
    for(j=0;j<10;j++) 
      cells[i][j] = NULL;

  pollingControl = [[UISegmentedControl alloc]
		     initWithFrame:CGRectMake(170.0f, 5.0f, 120.0f, 55.0f) ];
  [ pollingControl insertSegment:0 withTitle:@"OFF" animated: NO ];
  [ pollingControl insertSegment:1 withTitle:@"1 " animated: NO ];
  [ pollingControl insertSegment:2 withTitle:@"10 " animated: NO ];
  [ pollingControl selectSegment: preferences.polling ];

  zoomControl = [[UISegmentedControl alloc]
		  initWithFrame:CGRectMake(170.0f, 5.0f, 120.0f, 55.0f)];
  [ zoomControl insertSegment:0 withTitle:@"1" animated: NO ];
  [ zoomControl insertSegment:1 withTitle:@"2" animated: NO ];
  [ zoomControl insertSegment:2 withTitle:@"3" animated: NO ];
  [ zoomControl insertSegment:3 withTitle:@"4" animated: NO ];
  [ zoomControl insertSegment:4 withTitle:@"5" animated: NO ];
  [ zoomControl selectSegment: preferences.zoom ];

  distanceControl = [[UISegmentedControl alloc]
		      initWithFrame:CGRectMake(170.0f, 5.0f, 120.0f, 55.0f)];
  [ distanceControl insertSegment:0 withTitle:@"0" animated: NO ];
  [ distanceControl insertSegment:1 withTitle:@"10" animated: NO ];
  [ distanceControl insertSegment:2 withTitle:@"20" animated: NO ];
  [ distanceControl insertSegment:3 withTitle:@"30" animated: NO ];
  [ distanceControl insertSegment:4 withTitle:@"40" animated: NO ];
  [ distanceControl insertSegment:5 withTitle:@"50" animated: NO ];
  [ distanceControl selectSegment: preferences.movedist ];

  [ pref reloadData ];
  return pref;
}

- (int)numberOfGroupsInPreferencesTable:(UIPreferencesTable *)aTable {
  return 4;
}

- (void)tableRowSelected:(NSNotification *)notification
{
  if ([prefsTable selectedRow] == 8) { // Clear Cache
    [historyBrowser clearCacheFiles];
  }
  if ([prefsTable selectedRow] == 9) {   // Reload
    [historyBrowser loadURL:
		      [NSURL URLWithString:
			       @"file:///Applications/iSpotter.app/maps.html"]];
    // Back
    [self navigationBar: navBar buttonClicked: 1];
    //    [ navItem setTitle: @"History" ];
    //    tracking = YES;
  }

  if ([prefsTable selectedRow] == 15) { // Exit
    preferences.polling = [ pollingControl selectedSegment ];
    preferences.zoom = [ zoomControl selectedSegment ];
    preferences.ignore = [ ignoreControl value ];
    preferences.tracking = [ trackingControl value ];
    preferences.caching = [ cachingControl value ];
    preferences.movedist = [ distanceControl selectedSegment ];
    preferences.direct = [ directControl value ];
    preferences.pengine = [ pengineControl value ];
    preferences.wifi = [ wifiControl value ];
    savePreferences();
    exit(0);
  }
}

- (int)preferencesTable:(UIPreferencesTable *)aTable 
    numberOfRowsInGroup:(int)group 
{
  switch (group) { 
  case(0):
    return 2;
    break;
  case(1):
    return 6;
    break;
  case(2):
    return 2;
    break;
  case(3):
    return 2;
    break;
  }
}

- (UIPreferencesTableCell *)preferencesTable:(UIPreferencesTable *)aTable 
    cellForGroup:(int)group 
{
  if (groupcell[group] != NULL)
    return groupcell[group];

  groupcell[group] =  [[ UIPreferencesTextTableCell alloc ] 
			initWithFrame: CGRectMake(0, 0, 320, 30)];
  switch(group) {
  case(0):
    [ groupcell[group] setTitle: @"Detection Options" ];
    break;
  case(1):
    [ groupcell[group] setTitle: @"Map Options" ];
    break;
  case(2):
    [ groupcell[group] setTitle: @"Server Options" ];
    break;
  case(3):
    [ groupcell[group] setTitle: @"System" ];
    break;
  } 
  return groupcell[group];
}
  
- (float)preferencesTable:(UIPreferencesTable *)aTable 
    heightForRow:(int)row 
    inGroup:(int)group 
    withProposedHeight:(float)proposed 
{
    if (row == -1) {
        return 40;
    }
    return 55;
}

- (BOOL)preferencesTable:(UIPreferencesTable *)aTable 
    isLabelGroup:(int)group 
{
    return NO; 
}

- (UIPreferencesTableCell *)preferencesTable:(UIPreferencesTable *)aTable 
    cellForRow:(int)row 
    inGroup:(int)group 
{
  BOOL showSelection = NO;
  if (cells[row][group] != NULL)
    return cells[row][group];

  UIPreferencesTableCell *cell;
  cell = [ [ UIPreferencesTableCell alloc ] init ];

  switch (group) {
  case (0):
    switch (row) {
    case (0):
      [ cell setTitle:@"Auto Spot (min)" ];
      [ pollingControl selectSegment: preferences.polling];
      [ cell addSubview:pollingControl ];
      break;
    case (1):
      [ cell setTitle:@"Auto Enable WiFi" ];
      wifiControl = [ [ UISwitchControl alloc ]
		      initWithFrame:CGRectMake(170.0f, 15.0f, 120.0f, 30.0f)];
      [ wifiControl setValue: preferences.wifi ];
      [ cell addSubview: wifiControl ];
      break;
      //    case (1):
      //      [ cell setTitle:@"Ignore Far AP" ];
      //      ignoreControl = [ [ UISwitchControl alloc ]
      //			initWithFrame:CGRectMake(170.0f, 15.0f, 120.0f, 30.0f)];
      //      [ ignoreControl setValue: preferences.ignore ];
      //      [ cell addSubview: ignoreControl ];
      //      break;
    }
    break;
  case (1):
    switch (row) {
    case (0):
      [ cell setTitle:@"Map Zoom" ];
      [ zoomControl selectSegment: preferences.zoom ];
      [ cell addSubview: zoomControl ];
      break;
    case (1):
      [ cell setTitle:@"Keep Tracking" ];
      trackingControl = [ [ UISwitchControl alloc ]
			  initWithFrame:CGRectMake(170.0f, 15.0f, 120.0f, 30.0f)];
      [ trackingControl setValue: preferences.tracking ];
      [ cell addSubview: trackingControl ];
      break;
    case (2):
      [ cell setTitle:@"Log Distance(m)" ];

      // SliderControl is broken!?      
//       distanceControl = [[UISliderControl alloc]
// 			  initWithFrame: CGRectMake(170.0f, 15.0f, 120.0f, 30.0f)];
//       [distanceControl setValue: preferences.movedist ];
//       [distanceControl setMaxValue:200];
//       [distanceControl setMinValue:0];
//       [distanceControl setAllowsTickMarkValuesOnly: YES];
//       [distanceControl setNumberOfTickMarks: 4];
//       [distanceControl setContinuous: NO];
//       [distanceControl setShowValue:YES];
      [distanceControl selectSegment: preferences.movedist];
      [cell addSubview: distanceControl];
      break;
    case (3):
      [ cell setTitle:@"Map caching" ];
      cachingControl = [ [ UISwitchControl alloc ]
			  initWithFrame:CGRectMake(170.0f, 15.0f, 120.0f, 30.0f)];
      [ cachingControl setValue: preferences.caching ];
      [ cell addSubview: cachingControl ];
      break;
    case (4):
      [ cell setTitle:@"Clear Map Cache" ];
      break;
    case (5):
      [ cell setTitle:@"Reload Map" ];
      break;
    }
    break;
  case (2):
    switch (row) {
    case (0):
      [ cell setTitle:@"Accept Direct" ];
      directControl = [ [ UISwitchControl alloc ]
			initWithFrame:CGRectMake(170.0f, 15.0f, 120.0f, 30.0f)];
      [ directControl setValue: preferences.direct ];
      [ cell addSubview: directControl ];
      break;
    case (1):
      [ cell setTitle:@"Pretend PEngine" ];
      pengineControl = [ [ UISwitchControl alloc ]
			 initWithFrame:CGRectMake(170.0f, 15.0f, 120.0f, 30.0f)];
      [ pengineControl setValue: preferences.pengine ];
      [ cell addSubview: pengineControl ];
      break;
    }
    break;
  case (3):
    switch (row) {
    case (0):
      [ cell setTitle:@"Version" ];
      [ cell setValue:VERSION ];
      break;
    case (1):
      [ cell setTitle:@"Exit" ];
      showSelection = YES;
      break;
    }
  }
  if (!showSelection){
    [ cell setShowSelection: NO ];
  }
  cells[row][group] = cell;
  return cells[row][group];
}

@end
